package com.gitee.huanminabc.fastboot.web.servlet;

import com.alibaba.fastjson.JSONObject;
import com.gitee.huanminabc.fastboot.context.AppContext;
import com.gitee.huanminabc.fastboot.logback.LogTraceContextHolder;
import com.gitee.huanminabc.fastboot.web.context.ApiThreadContext;
import com.gitee.huanminabc.fastboot.web.context.AppWebContext;
import com.gitee.huanminabc.fastboot.web.exception.*;
import com.gitee.huanminabc.fastboot.web.validation.ValidationUtil;
import com.gitee.huanminabc.jcommon.base.SnowIdUtil;
import com.gitee.huanminabc.jcommon.base.UrlUtil;
import com.gitee.huanminabc.jnetty.server.handler.HttpInterfaceHandler;
import com.gitee.huanminabc.jnetty.utils.NettyHttpUtil;
import com.gitee.huanminabc.nullchain.common.NullResult;
import com.gitee.huanminabc.nullchain.enums.ResponseStatusEnum;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @program: netty-web
 * @description:
 * @author: huanmin
 * @create: 2025-03-31 14:00
 **/
@Slf4j
public class HttpInterfaceHandlerImpl implements HttpInterfaceHandler {

    @Override
    public boolean preHandler(ChannelHandlerContext ctx, FullHttpRequest request, String contentType, HttpMethod method, String uri, HttpHeaders headers, ByteBuf content) throws Throwable {
        log.info("请求路径: {} {}", method.name(), uri);
        //上下文绑定
        ApiThreadContext.build(request, ctx, contentType, uri);
        //检查是否可访问
        NullResult<Void> voidNullResult = loadCheckError();
        if (!voidNullResult.isSuccess()) {
            HttpResponseUtil.sendClient(voidNullResult);
            return false;
        }

        //日志链路id绑定
        String traceId = headers.get(LogTraceContextHolder.LOG_TRACE_ID_HEADER);
        if (traceId == null) {
            traceId = SnowIdUtil.uniqueLongHex();
        }
        LogTraceContextHolder.set(traceId);
        //拦截器处理
        for (HandlerInterceptor handlerInterceptor : AppWebContext.handlerInterceptors) {
            if (!handlerInterceptor.preHandle()) {
                return false;
            }
        }
        return true;
    }

    @Override
    public void handler(ChannelHandlerContext ctx, FullHttpRequest request, String contentType, HttpMethod method, String uri, HttpHeaders headers, ByteBuf content) throws Throwable {
        RequestMethodEnum methodEnum = RequestMethodEnum.getEnum(request.method().name());
        if (methodEnum == null) {
            throw new ApiProtocolNonSupportException(request.method().name());
        }
        String urlPath = UrlUtil.getUrlPath(uri);
        InterfaceMethodDefDesc interfaceMethodDefDesc = AppWebContext.httpInterfaceMethodDescMap.get(urlPath);
        if (interfaceMethodDefDesc == null) {
            throw new ApiMethodNotFoundException(methodEnum, urlPath);
        }
        //判断协议是否正确
        if (interfaceMethodDefDesc.getProtocol() != methodEnum) {
            throw new ApiProtocolErrorException(urlPath, methodEnum);
        }

        ApiThreadContext apiThreadContext = ApiThreadContext.get();
        apiThreadContext.setInterfaceMethodDefDesc(interfaceMethodDefDesc);
        JSONObject requestParams;
        switch (methodEnum) {
            case GET:
            case DELETE:
                requestParams = NettyHttpUtil.getRequestUrlParams(request);
                break;
            case POST:
            case PUT:
                requestParams = NettyHttpUtil.getRequestPostBody(request);
                break;
            default:
                throw new ApiProtocolNonSupportException(request.method().name());
        }
        Method methodDef = interfaceMethodDefDesc.getMethod();
        Object[] methodParameterBound = MethodParameterBound.getMethodParameterBound(requestParams, methodDef);
        //校验接口参数
        ValidationUtil.verifyMethod(methodDef, methodParameterBound);
        NullResult<?> result;
        try {
            Object value = methodDef.invoke(interfaceMethodDefDesc.getBean(), methodParameterBound);
            if (!NullResult.class.isAssignableFrom(methodDef.getReturnType())) {
                throw new ApiResultTypeException(interfaceMethodDefDesc);
            }
            result = (NullResult<?>) value;
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            //解包,不然出现异常的时候最外层的异常是InvocationTargetException 导致获取message是空的
            throw e.getCause();
        }
        if (result == null) {
            throw new ApiResultNullException(interfaceMethodDefDesc);
        }
        //拦截器处理
        for (HandlerInterceptor handlerInterceptor : AppWebContext.handlerInterceptors) {
            result = handlerInterceptor.postHandle(result);
        }
        HttpResponseUtil.sendClient(result);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, FullHttpRequest req, String contentType, HttpMethod method, String uriStr, HttpHeaders headers, ByteBuf content, Throwable cause) {
        log.error("", cause);
        NullResult<?> nullResult = InterfaceExceptionEventFactory.executeEvent(cause);
        NettyHttpUtil.Response(ctx, NettyHttpUtil.JSON_CONTENT_TYPE_UTF_8,
                Unpooled.copiedBuffer(JSONObject.toJSONString(nullResult), CharsetUtil.UTF_8)
                , null, nullResult.isSuccess() ? HttpResponseStatus.OK : HttpResponseStatus.INTERNAL_SERVER_ERROR
        );
    }

    @Override
    public void postHandler(ChannelHandlerContext ctx, FullHttpRequest req, String s, HttpMethod method, String uriStr, HttpHeaders headers, ByteBuf content, long executeTime) {
        log.info("请求路径: {} {},请求执行完毕总耗时:{}ms", method.name(), uriStr, executeTime);
        LogTraceContextHolder.remove();
        ApiThreadContext.clear();
        for (HandlerInterceptor handlerInterceptor : AppWebContext.handlerInterceptors) {
            handlerInterceptor.afterCompletion();
        }
    }

    //未准备好的错误回复
    private NullResult<Void> loadCheckError() {
        if (AppContext.projectLoadOk) {
            return NullResult.success();
        }
        return NullResult.error(ResponseStatusEnum.SERVICE_UNAVAILABLE, "服务未准备好,请稍后再试");
    }
}
